// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 垃圾收集器：写屏障。
// 
// 对于并发垃圾收集器，Go编译器通过
// 发出写屏障调用来实现对堆对象中可能存在的指针值字段的更新。
// 单个指针写入的主要写屏障是gcWriteBarrier，在
// 程序集中实现。此文件包含批量
// 操作的写屏障入口点。另见MWBUF。去

package runtime

import (
	"internal/abi"
	"internal/goarch"
	"unsafe"
)

// Go使用了一个混合屏障，它结合了Yuasa风格的删除
// barrier，对引用被
// 覆盖了Dijkstra插入屏障，对引用被写入的
// 对象进行了着色。当调用goroutine的堆栈为灰色时，barrier 
// 的插入部分是必需的。在
// 伪代码中，障碍是：
// 
// writePointer（slot，ptr）：
// shade（*slot）
// 如果当前堆栈为灰色：
// shade（ptr）
// /*slot=ptr 
// 
// slot是Go代码中的目标。
// ptr是进入Go代码插槽的值。
// 
// Shade通过将引用的
// 添加到wbuf并对其进行标记，表示它看到了一个白色指针。
// 
// 这两个阴影和条件共同作用，防止变异子
// 从垃圾收集器隐藏对象：
// 
// 1。shade（*slot）通过将
// 指向某个对象的唯一指针从堆移动到其堆栈，防止变异子隐藏该对象。如果它尝试
// 从堆中取消对象的链接，这将使其变暗。
// 
// 2。shade（ptr）通过将
// 指向它的唯一指针从堆栈移动到
// 堆中的黑色对象中，防止变异子隐藏对象。如果它试图将指针安装到黑色对象中，
// 这将使其变暗。
// 
// 3。一旦goroutine的堆栈变黑，阴影（ptr）就变得不必要了。shade（ptr）通过将对象从堆栈移动到堆来防止隐藏对象，但这需要首先在堆栈上隐藏一个指针。扫描堆栈后，它立即只
// 指向着色对象，因此它不隐藏任何内容，
// 着色（*槽）防止它隐藏其
// 堆栈上的任何其他指针。
// 
// 有关此屏障的详细说明和
// 正确性的证明，请参阅https:
// 
// 
// 
// 处理内存顺序：
// 
// Yuasa和Dijkstra屏障都可以根据包含插槽的对象的
// 颜色设置。我们选择不让这些
// 成为有条件的，因为确保持有
// 的对象在没有变异子
// 的情况下不会同时改变插槽颜色的成本似乎太高了。ABCFDG 
// 
// 最初[slot]和[slotmark]都是0（零）
// Mutator thread GC thread 
// st[slot]，ptr st[slotmark]，1 
// 
// ld r1[slotmark ld r2]
// 
// 在st和ld之间没有昂贵的内存屏障，大多数硬件（包括386/amd64）上的最终
// 结果可以是r1==r2==0。这是一个经典的
// 示例，说明了当允许使用旧的
// 存储对负载进行重新排序时会发生什么（避免这种重新排序是经典的
// 彼得森/德克尔互斥算法的核心）。我们不需要内存
// 屏障，这会减慢突变子和GC的速度，而总是将ptr对象变成灰色
// 无论插槽的颜色如何。
// 
// 我们故意忽略记忆障碍的另一个地方是当
// 访问mheap_u2;。arena_用于检查指针是否指向
// 堆。在宽松的内存机器上，变种人可以通过更新使用的arena_来扩展堆的大小，从这个新区域分配一个对象，并发布一个指向该对象的指针，但是在另一个处理器上运行的跟踪可以观察指针
// 但是使用arena_的旧值。在这种情况下，跟踪将不会标记该对象，即使它是可访问的。但是，当变量
// 发布
// 指针时，它保证执行写屏障，因此它将负责标记对象。一般的
// 结果是垃圾收集器可能会缓存mheap_200;的
// 值。你使用的竞技场。（见第9984期。）
// 
// 
// 堆栈写入：
// 
// 编译器会忽略写入当前帧的写屏障，
// 但是如果堆栈指针已向下传递到调用堆栈，
// 编译器将为通过该
// 指针的写入生成一个写屏障（因为它不知道它不是堆指针）。
// 
// 如果插槽将
// 指向堆栈，可能会试图忽略写屏障。别这样！Mark termination仅重新扫描自并发扫描以来可能处于活动状态的帧，因此它依赖于写屏障来跟踪尚未处于活动状态的堆栈帧中指针的更改。
// 
// 
// 全局写入：
// 
// 当堆指针
// 存储在全局中时，Go垃圾收集器需要写屏障。许多垃圾收集器忽略对
// 全局的写入，而是在
// 终止期间拾取全局->堆指针。这增加了暂停时间，因此我们转而依赖写入
// 障碍来写入全局，这样我们就不必在标记终止期间重新扫描
// 全局。
// 
// 
// 发布顺序：
// 
// 写入障碍是*预发布*，这意味着写入
// 障碍发生在*slot=ptr write之前，这可能使某些当前无法访问它的goroutine可以访问ptr 
// 。
// 
// 
// 信号处理程序指针写入：
// 
// 一般来说，信号处理程序无法安全地调用写入
// 屏障，因为它可能在没有P的情况下运行，甚至在写入
// 屏障期间运行。
// 
// 只有一个例外：profbuf。go在
// 信号处理器配置文件记录期间忽略了一个屏障。这是安全的，因为
// 删除障碍。见profbuf。进行详细的辩论。如果我们去掉删除障碍，我们就必须想出一种新的方法来处理配置文件记录。

// typedmemmove将类型t的值从src复制到dst。
// 必须是nosplit，请参见#16026。
// 
// TODO:非常适合go:nosplitrec，因为我们在散装屏障或memmove中的任何地方都没有安全点。
// 
// go:nosplit 
func typedmemmove(typ *_type, dst, src unsafe.Pointer) {
	if dst == src {
		return
	}
	if writeBarrier.needed && typ.ptrdata != 0 {
		bulkBarrierPreWrite(uintptr(dst), uintptr(src), typ.ptrdata)
	}
	// 这里有一个竞赛：如果其他goroutine可以写入
	// src，它可能会在我们执行写屏障后，但在我们执行
	// 内存复制之前，更改src中的一些指针。这是安全的，因为该
	// 其他goroutine执行的写操作也必须伴随着写
	// 屏障，所以在最坏的情况下，我们不必要地将src中的旧
	// 指针变灰。
	memmove(dst, src, typ.size)
	if writeBarrier.cgo {
		cgoCheckMemmove(typ, dst, src, 0, typ.size)
	}
}

// go:linkname reflect_typedmemmove reflect。typedmemmove 
func reflect_typedmemmove(typ *_type, dst, src unsafe.Pointer) {
	if raceenabled {
		raceWriteObjectPC(typ, dst, getcallerpc(), abi.FuncPCABIInternal(reflect_typedmemmove))
		raceReadObjectPC(typ, src, getcallerpc(), abi.FuncPCABIInternal(reflect_typedmemmove))
	}
	if msanenabled {
		msanwrite(dst, typ.size)
		msanread(src, typ.size)
	}
	if asanenabled {
		asanwrite(dst, typ.size)
		asanread(src, typ.size)
	}
	typedmemmove(typ, dst, src)
}

// go:linkname reflectlite_typedmemmove internal/reflectlite。typedmemmove 
func reflectlite_typedmemmove(typ *_type, dst, src unsafe.Pointer) {
	reflect_typedmemmove(typ, dst, src)
}

// typedmemmovepartial与typedmemmove类似，但假设
// dst和src将字节指向值中，只复制大小字节。
// off必须是goarch的倍数。PtrSize。
// go:linkname reflect_typedmemmovepartial reflect。reflectcall调用typedmemmovepartial 
func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
	if writeBarrier.needed && typ.ptrdata > off && size >= goarch.PtrSize {
		if off&(goarch.PtrSize-1) != 0 {
			panic("reflect: internal error: misaligned offset")
		}
		pwsize := alignDown(size, goarch.PtrSize)
		if poff := typ.ptrdata - off; pwsize > poff {
			pwsize = poff
		}
		bulkBarrierPreWrite(uintptr(dst), uintptr(src), pwsize)
	}

	memmove(dst, src, size)
	if writeBarrier.cgo {
		cgoCheckMemmove(typ, dst, src, off, size)
	}
}

// reflectcallmove将返回值
// 从堆栈复制到堆中，并调用必要的写
// 屏障。dst、src和size描述
// copy的返回值区域。typ描述整个帧（不仅仅是返回值）。
// typ可能为零，表示不需要写屏障。
// 
// 它必须是nosplit，并且只能调用nosplit函数，因为reflectcall的堆栈映射错误。
// 
// go:nosplit 
func reflectcallmove(typ *_type, dst, src unsafe.Pointer, size uintptr, regs *abi.RegArgs) {
	if writeBarrier.needed && typ != nil && typ.ptrdata != 0 && size >= goarch.PtrSize {
		bulkBarrierPreWrite(uintptr(dst), uintptr(src), size)
	}
	memmove(dst, src, size)

	// 将寄存器中返回的指针移动到GC可以看到它们的位置。
	for i := range regs.Ints {
		if regs.ReturnIsPtr.Get(i) {
			regs.Ptrs[i] = unsafe.Pointer(regs.Ints[i])
		}
	}
}

// go:nosplit 
func typedslicecopy(typ *_type, dstPtr unsafe.Pointer, dstLen int, srcPtr unsafe.Pointer, srcLen int) int {
	n := dstLen
	if n > srcLen {
		n = srcLen
	}
	if n == 0 {
		return 0
	}

	// 编译器在
	// 指令插入运行之前发出对typedslicecopy的调用，因此与其他复制和
	// 赋值操作不同，它不在调用
	// 代码中指令插入，需要自己的指令插入。
	if raceenabled {
		callerpc := getcallerpc()
		pc := abi.FuncPCABIInternal(slicecopy)
		racewriterangepc(dstPtr, uintptr(n)*typ.size, callerpc, pc)
		racereadrangepc(srcPtr, uintptr(n)*typ.size, callerpc, pc)
	}
	if msanenabled {
		msanwrite(dstPtr, uintptr(n)*typ.size)
		msanread(srcPtr, uintptr(n)*typ.size)
	}
	if asanenabled {
		asanwrite(dstPtr, uintptr(n)*typ.size)
		asanread(srcPtr, uintptr(n)*typ.size)
	}

	if writeBarrier.cgo {
		cgoCheckSliceCopy(typ, dstPtr, srcPtr, n)
	}

	if dstPtr == srcPtr {
		return n
	}

	// 注意：检查类型没有意义。这里的ptrdata:
	// 编译器只对带有指针的类型发出对typedslicecopy的调用，
	// 并在调用typedslicecopy之前对指针进行GrowtSlice和reflect_typedslicecopy检查
	// 。
	size := uintptr(n) * typ.size
	if writeBarrier.needed {
		pwsize := size - typ.size + typ.ptrdata
		bulkBarrierPreWrite(uintptr(dstPtr), uintptr(srcPtr), pwsize)
	}
	// 有关
	// 屏障和memmove之间竞争的讨论，请参见typedmemmove。
	memmove(dstPtr, srcPtr, size)
	return n
}

// go:linkname reflect_typedslicecompy reflect。typedslicecopy 
func reflect_typedslicecopy(elemType *_type, dst, src slice) int {
	if elemType.ptrdata == 0 {
		return slicecopy(dst.array, dst.len, src.array, src.len, elemType.size)
	}
	return typedslicecopy(elemType, dst.array, dst.len, src.array, src.len)
}

// typedmemclr使用typ类型清除ptr处的类型化内存。ptr上的
// 内存必须已经初始化（因此处于类型安全
// 状态）。如果是第一次初始化内存，请参阅
// memclrNoHeapPointers。
// 
// 如果调用者知道typ有指针，它也可以调用memclrHasPointers。
// 
// go:nosplit 
func typedmemclr(typ *_type, ptr unsafe.Pointer) {
	if writeBarrier.needed && typ.ptrdata != 0 {
		bulkBarrierPreWrite(uintptr(ptr), 0, typ.ptrdata)
	}
	memclrNoHeapPointers(ptr, typ.size)
}

// go:linkname reflect_typedmemclr reflect。typedmemclr 
func reflect_typedmemclr(typ *_type, ptr unsafe.Pointer) {
	typedmemclr(typ, ptr)
}

// go:linkname reflect_typedmemclr partial reflect。typedmemclrpartial 
func reflect_typedmemclrpartial(typ *_type, ptr unsafe.Pointer, off, size uintptr) {
	if writeBarrier.needed && typ.ptrdata != 0 {
		bulkBarrierPreWrite(uintptr(ptr), 0, size)
	}
	memclrNoHeapPointers(ptr, size)
}

// memclrHasPointers从ptr开始清除n字节的类型化内存。
// 调用者必须确保ptr处的对象类型具有
// 指针，通常通过检查typ。ptrdata。但是，ptr 
// 不必指向分配的开始。
// 
// go:nosplit 
func memclrHasPointers(ptr unsafe.Pointer, n uintptr) {
	bulkBarrierPreWrite(uintptr(ptr), 0, n)
	memclrNoHeapPointers(ptr, n)
}
